package com.googlecode.dgwt.client.dojo.data.base;

import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import com.googlecode.dgwt.client.dojo.Dojo;
import com.googlecode.dgwt.client.dojo.callback.SimpleCallback;
import com.googlecode.dgwt.client.dojo.callback.VoidCallback;
import com.googlecode.dgwt.client.dojo.data.listener.AttachListener;
import com.googlecode.dgwt.client.dojo.data.listener.DataListener;

/**
 * @author wellsantos@wellsantos.com
 * @since 0.01
 */
public abstract class StoreBase<Item> extends AbstractList<Item> implements Store<Item> {
    
    private boolean readOnly;
    private StoreEngine<Item> engine;    
    
    
    protected StoreBase(int initialCapacity) {
        this.engine = createEngine(initialCapacity);
        this.initialize();
    }
    
    protected StoreBase(StoreBase<Item> mirroredStore) {
        this.engine = new MirroredStoreEngine<Item>(this, mirroredStore);
        this.initialize();
    }
    

    protected abstract StoreEngine<Item> createEngine(int initialCapacity);
    
    
    protected void initialize(){        
    }
    
    protected void setMirroredStore(StoreBase<Item> mirroredStore){
        assert (isMirror()) : "can't define a new mirrored store. This store is not a mirror.";
        ((MirroredStoreEngine<Item>)this.engine).setMirroredStore(mirroredStore);
    }
    
    protected boolean isMirror(){
        return engine instanceof MirroredStoreEngine<?>;
    }

    protected StoreEngine<Item> getEngine() {
        return engine;
    }
    
    @Override
    public Store<Item> cloneStore() {
        StoreBase<Item> store = (StoreBase<Item>) this.createMirror();
        store.engine = store.createEngine(store.size());
        store.getEngine().getItemList().addAll(this);
        return store;
    }
    
    @Override
    public Store<Item> append(Item item) {
        add(item);
        return this;
    }

    @Override
    public Item get(int index) {
        return getEngine().get(index);
    }

    @Override
    public boolean add(Item item) {
        if (!isReadOnly()) {
            return getEngine().add(item);
        }
        return false;
    }

    @Override
    public boolean remove(Object item) {
        if (!isReadOnly()) {
            return getEngine().remove(item);
        }
        return false;
    }

    @Override
    public void add(int index, Item element) {
        if (!isReadOnly()) {
            getEngine().add(index, element);
        }
    }
    
    @Override
    public Item remove(int index) {
        if (!isReadOnly()) {
            return getEngine().remove(index);
        }
        return null;
    }

    @Override
    public Item set(int index, Item element) {
        if (!isReadOnly()) {
            return getEngine().set(index, element);
        }
        return null;
    }

    @Override
    public boolean contains(Object item) {
        return getEngine().getItemList().contains(item);
    }

    @Override
    public boolean isEmpty() {
        return getEngine().getItemList().isEmpty();
    }

    @Override
    public Iterator<Item> iterator() {
        return getEngine().getItemList().iterator();
    }

    @Override
    public int size() {
        return getEngine().getItemList().size();
    }

    @Override
    public Object[] toArray() {
        return getEngine().getItemList().toArray();
    }

    @Override
    public <T> T[] toArray(T[] a) {
        return getEngine().getItemList().toArray(a);
    }

    @Override
    public String toString() {
        return Dojo.reflect.getSimpleName(getClass()) + getEngine().getItemList().toString();
    }

    @Override
    public boolean addListener(DataListener<Item> listener) {
        return getEngine().getDataListenerList().add(listener);
    }
 
    @Override
    public boolean removeListener(AttachListener<?> listener) {
        return getEngine().getDataListenerList().remove(listener);
    }
    
    @Override
    public boolean hasListener(AttachListener<?> listener) {
        return getEngine().getDataListenerList().contains(listener);        
    }
   
    @Override
    public Set<DataListener<Item>> getDataListeners(){
        return getEngine().getDataListenerList();
    }

    @Override
    public void fetch(int index, int count, VoidCallback callback) {
        callback.callback();
    }

    @Override
    public void fetch(int index, int count, SimpleCallback<List<Item>> callback) {
        int size = size();
        
        if (index < 0) {
            index = 0;
        }
        if (index >= size) {
            index = 0;
        }
        if (index + count > size) {
            count = size - index;
        }
        
        List<Item> items = new ArrayList<Item>(count);
        
        int i=0;
        size = index + count;
        
        for (Item item : this) {
            if (i >= index) {
                
                items.add(item);
                
                if (i+1 >= size) {
                    break;
                }
            }
            i++;
        }
        
        callback.callback(items);
    }

    // FIXME por public em classes finais
    protected void setReadOnly(boolean readOnly) {
        this.readOnly = readOnly;
    }

    @Override
    public boolean isReadOnly() {
        return readOnly;
    }

    @Override
    public void refresh() {
        getEngine().onRefresh();
    }

    @Override
    public void refreshItem(Item item) {
        getEngine().onRefreshItem(item);
    }

    @Override
    public void refreshValue(Item item, int columnIndex) {
        getEngine().onRefreshValue(item, columnIndex);
    }

    @Override
    public boolean replace(Item curItem, Item newItem) {
        if (curItem != null && newItem != null) {
            if ((curItem == newItem 
                    || (curItem != null && curItem.equals(newItem)))
                    && contains(curItem)) {
                getEngine().onModified(curItem);
                return true;
            }
            remove(curItem);
            add(newItem);
            return true;
        }
        return false;
    }
    
    @Override
    public boolean modified(Item item) {
        if (contains(item)){
            getEngine().onModified(item);
            return true;
        }
        return false;
    }

    @Override
    public boolean equals(Object o) {
        return this == o;
    }

    @Override
    public int hashCode() {
        return System.identityHashCode(this);
    }

}
